fn copy() {
    let mut x = String::from("hello");
    let y = x.clone();

    x.push_str(", world");
    println!("\n------------   Test Copy   ------------");
    println!("x: {}, y: {}", x, y);
}

fn gives_ownership() -> String {
    let some_string = String::from("hello");
    some_string
}

fn takes_and_gives_back(a_string: String) -> String {
    a_string
}

fn test_ownership() {
    let s1 = gives_ownership();  // gives_ownership moves its return value to s1
    let s2 = String::from("hello");
    let s3 = takes_and_gives_back(s2);

    println!("\n----------- Test Ownership ------------");
    println!("s1: {}", s1);
    // println!("s2: {}", s2); // error: s2 value borrowed here after move
    println!("s3: {}", s3);
}

fn immut_reference(im_v: &String) {
    println!("im_v: {}", im_v);
}

fn mut_reference(m_v: &mut String) {
    m_v.push_str(", world");
    println!("m_v: {}", m_v);
}

fn test_reference() {
    println!("\n----------- Test Reference ------------");
    let x = String::from("hello");
    immut_reference(&x);

    let mut y = String::from("hello");
    mut_reference(&mut y);
    println!("x: {}", x);
    println!("y: {}", y);
}

fn main() {
    copy();
    test_ownership();
    test_reference();
}
